package com.yy.permissions_general.common.aopSubmit.aspect;

import com.yy.permissions_general.common.aopSubmit.aop.NoRepeatSubmit;
import com.yy.permissions_general.common.aopSubmit.config.ExpressionEvaluator;
import com.yy.permissions_general.common.config.exception.base.ServiceException;
import com.yy.permissions_general.utils.RedisUtil;
import com.yy.permissions_general.utils.SecurityUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.expression.AnnotatedElementKey;
import org.springframework.expression.EvaluationContext;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

/**
 * 防止订单重复提交
 */
@Slf4j
@Aspect
@Component
public class RepeatSubmitAspect {
    @Autowired
    private RedisUtil redisUtil;

    private final ExpressionEvaluator<String> evaluator = new ExpressionEvaluator<>();

    /**
     * 方法上有@RedisSynchronized注解均会调用此方法
     *
     * @param point
     * @param noRepeatSubmit
     * @return
     */
    @Around("@annotation(noRepeatSubmit)")
    @SneakyThrows
    public Object around(ProceedingJoinPoint point, NoRepeatSubmit noRepeatSubmit) {
        // 获取自定义key，作为redis的key
        String key = getKey(point);
        // 获取请求设置多少秒不可重复提交(redis过期时间)
        int second = noRepeatSubmit.second();
        // 获取异常提示信息
        String msg = noRepeatSubmit.msg();
        // 获取是否需要在key上拼接用户ID
        boolean splicingUserId = noRepeatSubmit.splicingUserId();
        if (splicingUserId) {
            Integer userId = SecurityUtil.getCurrentUser().getId();
            key += "-" + userId;
        }
        // 根据当前key查询redis是否有值
        Object o = redisUtil.get("repeatSubmit:"+key);
        if (o == null) {
            // 没值，继续进入接口，将当前key存入redis，指定过期时间
            redisUtil.set("repeatSubmit:"+key, "checkSubmit", second, TimeUnit.SECONDS);
            return point.proceed();
        } else {
            // 有值，此处抛出全局异常
            throw new ServiceException(500,msg);
        }
    }



    /**
     * SpEL 表达式的解析
     *
     * @param point
     * @return
     */
    public String getKey(ProceedingJoinPoint point) {
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        NoRepeatSubmit redisSynchronized = method.getAnnotation(NoRepeatSubmit.class);
        if (point.getArgs() == null) {
            return null;
        }
        EvaluationContext evaluationContext = evaluator.createEvaluationContext(point.getTarget(), point.getTarget().getClass(), ((MethodSignature) point.getSignature()).getMethod(), point.getArgs());
        AnnotatedElementKey methodKey = new AnnotatedElementKey(((MethodSignature) point.getSignature()).getMethod(), point.getTarget().getClass());
        return evaluator.condition(redisSynchronized.key(), methodKey, evaluationContext, String.class);
    }

}